import {
    AttackInfo,
    AttackType,
    ConstructInfo,
    DamageInfo,
    DamageType,
    DeadInfo,
    InputInfo,
    ITriggerAdapter,
    KeyEventInfo,
    LevelUpInfo,
    ManipulatingItemInfo,
    MouseEventInfo,
    MouseEventType,
    PlayerEvent,
    PlayerExitInfo,
    PlayerUnitEvent,
    SelectInfo,
    SellItemInfo,
    SpellInfo,
    SyncDataInfo,
    TransInfo,
    triggetAdapter,
    UnitEvent, UpdateInfo,
    WheelInfo
} from "../../adapterInterface/model4/ITriggerAdapter";
import {PlayerModel} from "../../../adapterModel/model4/PlayerModel";
import {rectMap, RectRecord} from "../model2/RectAdapter";
import {UnitModel} from "../../../adapterModel/model3/UnitModel";
import {GlobalModel} from "../../../adapterModel/model1/GlobalModel";
import {ModelFactory} from "../../../adapterModel/model5/ModelFactory";
import {ModelType} from "../../adapterInterface/model5/IModelFactory";
import {HurtType} from "../../../../dataSource/DataModel";
import {PlayerFactory} from "../../../../PlayerFactory";
import {ItemModel} from "../../../adapterModel/model3/ItemModel";
import {RectModel} from "../../../adapterModel/model2/RectModel";
import {Logger} from "../../../config/Logger";
import {LocModel} from "../../../base/LocModel";
import {FrameEventUtil} from "../model1/FrameAdapter";


export let mouseEventType: MouseEventType = MouseEventType.mouseMove;


export enum MouseAction {
    mouseUp,
    mouseDown
}

export enum MouseStatus {
    mouseLeft = 1,
    mouseRight
}

export class TriggerAdapter implements ITriggerAdapter {
    addAction(handle: any, actionFunc: (this: void) => void): void {
        TriggerAddAction(handle, actionFunc);
    }

    bindHandle(handle: any): void {
        handle_ref(handle);
    }

    createHandle(): any {
        return CreateTrigger();
    }

    destroy(handle: any): void {
        this.reset(handle);
        handle_unref(handle);
        DestroyTrigger(handle);
    }

    equal(handle1: any, handle2: any): boolean {
        return (GetHandleId(handle1) === GetHandleId(handle2))
    }

    isValid(handle: any): boolean {
        if (handle) {
            return true;
        }
        return false;
    }

    registerWindowResizeEvent(handle: any, sync: boolean, fun: (this: void) => void) {
        DzTriggerRegisterWindowResizeEventByCode(handle, sync, fun);
    }

    registerAnyPlayerChatEvent(handle: any, players: PlayerModel[], chatMessageToDetect: string, exactMatchOnly: boolean): void {
        for (let i = 0; i < players.length; i++) {
            const player = players[i];
            TriggerRegisterPlayerChatEvent(handle, player.handle, chatMessageToDetect, exactMatchOnly);
        }
    }

    registerAnyPlayerEvent(handle: any, players: PlayerModel[], playerEvent: PlayerEvent): void {
        let newPlayerEvent;
        switch (playerEvent) {
            case PlayerEvent.playerLeave: {
                newPlayerEvent = EVENT_PLAYER_LEAVE;
                break;
            }
        }
        if (newPlayerEvent) {
            for (let i = 0; i < players.length; i++) {
                const player = players[i];
                TriggerRegisterPlayerEvent(handle, player.handle, newPlayerEvent);
            }
        }
    }

    registerAnyUnitEvent(handle: any, whichPlayerUnitEvent: PlayerUnitEvent): void {
        let newPlayerUnitEvent;
        switch (whichPlayerUnitEvent) {
            case PlayerUnitEvent.constructFinish: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_CONSTRUCT_FINISH;
                break;
            }
            case PlayerUnitEvent.unitAttacked: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_ATTACKED;
                break;
            }
            case PlayerUnitEvent.unitDead: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_DEATH;
                break;
            }
            case PlayerUnitEvent.unitLevelUp: {
                newPlayerUnitEvent = EVENT_PLAYER_HERO_LEVEL;
                break;
            }
            case PlayerUnitEvent.unitFinishUseSpell: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_SPELL_EFFECT;
                break;
            }
            case PlayerUnitEvent.unitBeforeUseSpell: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_SPELL_CHANNEL;
                break;
            }
            case PlayerUnitEvent.unitGetItem: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_PICKUP_ITEM;
                break;
            }
            case PlayerUnitEvent.unitUseItem: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_USE_ITEM;
                break;
            }
            case PlayerUnitEvent.unitDropItem: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_DROP_ITEM;
                break;
            }
            case PlayerUnitEvent.unitBuyItem: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_SELL_ITEM;
                break;
            }
            case PlayerUnitEvent.unitSellItem: {
                newPlayerUnitEvent = EVENT_PLAYER_UNIT_PAWN_ITEM;
                break;
            }
        }
        if (newPlayerUnitEvent) {
            TriggerRegisterAnyUnitEventBJ(handle, newPlayerUnitEvent)
        }
    }

    registerCommandEvent(handle: any, whichAbility: number, order: string): void {
        TriggerRegisterCommandEvent(handle, whichAbility, order);
    }

    registerEnterRect(handle: any, whichRect: RectRecord): void {
        TriggerRegisterEnterRegion(handle, whichRect.region, null);
    }

    registerKeyEvent(handle: any, key: number, state: number, sync: boolean, fun: (this: void) => void): void {
        DzTriggerRegisterKeyEventByCode(handle, key, state, sync, fun);
    }

    registerLeaveRect(handle: any, whichRect: RectRecord): void {
        TriggerRegisterLeaveRegion(handle, whichRect.region, null);
    }

    registerMouseWheel(handle: any, callBack: (this: void, bStop: boolean) => void, sync: boolean): void {
        DzTriggerRegisterMouseWheelEventByCode(handle, sync, function (this: void) {
            mouseEventType = MouseEventType.mouseRightDown;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            const percentX = mouseInfo.percentX;
            const percentY = mouseInfo.percentY;
            const wheelInfo = triggetAdapter.adapter.getWheelInfo();
            const del = wheelInfo.delta;

            const ret = FrameEventUtil.exeMouseWheelScript({ x: percentX, y: percentY, del, player: mouseInfo.player })
            callBack(ret);
        });
    }

    registerUpdate(handle: any, callBack: (this: void) => void): void {
        DzFrameSetUpdateCallbackByCode(callBack);
    }

    registerMouseEvent(handle: any, sync: boolean, fun: (this: void, mouseInfo: MouseEventInfo) => void): void {
        DzTriggerRegisterMouseMoveEventByCode(handle, false, function (this: void) {
            mouseEventType = MouseEventType.mouseMove;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            fun(mouseInfo);
        });
        DzTriggerRegisterMouseEventByCode(handle, MouseStatus.mouseRight, MouseAction.mouseDown, false, function (this: void) {
            mouseEventType = MouseEventType.mouseRightDown;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            const percentX = mouseInfo.percentX;
            const percentY = mouseInfo.percentY;

            const ret = FrameEventUtil.exeMouseRightDownScript({ x: percentX, y: percentY, player: mouseInfo.player })
            mouseInfo.bStop = ret;
            fun(mouseInfo);
        });
        DzTriggerRegisterMouseEventByCode(handle, MouseStatus.mouseLeft, MouseAction.mouseDown, false, function (this: void) {
            // Logger.toastError(`${FrameEventUtil.clickOnUi}`)
            if (FrameEventUtil.clickOnUi) {
                return;
            }
            mouseEventType = MouseEventType.mouseLeftDown;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            fun(mouseInfo);
        });
        DzTriggerRegisterMouseEventByCode(handle, MouseStatus.mouseRight, MouseAction.mouseUp, false, function (this: void) {
            mouseEventType = MouseEventType.mouseRightUp;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            fun(mouseInfo);
        });
        DzTriggerRegisterMouseEventByCode(handle, MouseStatus.mouseLeft, MouseAction.mouseUp, false, function (this: void) {
            mouseEventType = MouseEventType.mouseLeftUp;
            const mouseInfo = triggetAdapter.adapter.getMouseEventInfo();
            fun(mouseInfo);
        });
    }

    registerPlayerSelected(handle: any, players: PlayerModel[], selected: boolean): void {
        for (let i = 0; i < players.length; i++) {
            const player = players[i];
            TriggerRegisterPlayerSelectionEventBJ(handle, player.handle, selected);
        }
    }

    registerSyncData(handle: any, key: string): void {
        DzTriggerRegisterSyncData(handle, key, false)
    }

    registerUnitEvent(handle: any, whichUnit: UnitModel, whichEvent: UnitEvent): void {
        let newPlayerUnitEvent;
        switch (whichEvent) {
            case UnitEvent.unitDamage: {
                newPlayerUnitEvent = EVENT_UNIT_DAMAGED;
                break;
            }
        }
        if (newPlayerUnitEvent) {
            TriggerRegisterUnitEvent(handle, whichUnit.handle, newPlayerUnitEvent);
        }
    }

    reset(handle: any): void {
        ResetTrigger(handle);
    }


    getAttackInfo(): AttackInfo {
        const attacker = GetAttacker();
        const attacked = GetAttackedUnitBJ()
        const attackerId = GlobalModel.getHandleId(attacker);
        const attackedId = GlobalModel.getHandleId(attacked);
        const attackerModel: UnitModel = ModelFactory.getInstance().getModel(attackerId, ModelType.unit) as UnitModel;
        const attackedModel: UnitModel = ModelFactory.getInstance().getModel(attackedId, ModelType.unit) as UnitModel;
        return { attacked: attackedModel, attacker: attackerModel };
    }

    getConstructInfo(): ConstructInfo {
        const activeUnit = GetConstructedStructure();
        const id = GlobalModel.getHandleId(activeUnit);
        let unitModel: UnitModel = ModelFactory.getInstance().getModel(id, ModelType.unit) as UnitModel;
        if (unitModel) {
        } else {
            ModelFactory.getInstance().registUnit(activeUnit);
            unitModel = ModelFactory.getInstance().getModel(id, ModelType.unit) as UnitModel;
        }
        return {constructUnit: unitModel};
    }

    getDamageInfo(): DamageInfo {
        let attacker = GetEventDamageSource();
        const attacked = GetTriggerUnit();
        const damage = GetEventDamage();
        const attackType = EXGetEventDamageData(DamageType.EVENT_DAMAGE_DATA_ATTACK_TYPE);
        let hurtType;
        if (attackType === AttackType.ATTACK_TYPE_NORMAL) {
            hurtType = HurtType.spell;
        } else if (attackType === AttackType.ATTACK_TYPE_MELEE) {
            hurtType = HurtType.physics;
        } else if (attackType === AttackType.ATTACK_TYPE_HERO) {
            hurtType = HurtType.hero;
        }
        const attackerId = GlobalModel.getHandleId(attacker);
        const attackedId = GlobalModel.getHandleId(attacked);

        const attackerModel: UnitModel = ModelFactory.getInstance().getModel(attackerId, ModelType.unit) as UnitModel;
        const attackedModel: UnitModel = ModelFactory.getInstance().getModel(attackedId, ModelType.unit) as UnitModel;

        return { attacker: attackerModel, attacked: attackedModel, damage, hurtType };
    }

    getDeadInfo(): DeadInfo {
        const killerUnit = GetKillingUnit();
        const killedUnit = GetDyingUnit();
        const killerUnitId = GlobalModel.getHandleId(killerUnit);
        const killedUnitId = GlobalModel.getHandleId(killedUnit);

        const killerUnitModel: UnitModel = ModelFactory.getInstance().getModel(killerUnitId, ModelType.unit) as UnitModel;
        const killedUnitModel: UnitModel = ModelFactory.getInstance().getModel(killedUnitId, ModelType.unit) as UnitModel;

        return { killer: killerUnitModel, killed: killedUnitModel };
    }

    getInputInfo(): InputInfo {
        const str = GetEventPlayerChatString();
        const player = GetTriggerPlayer();
        const playerId = GetPlayerId(player);
        const playerModel: PlayerModel = PlayerFactory.getInstance().getPlayer(playerId)
        return { player: playerModel, str };
    }

    getKeyEventInfo(): KeyEventInfo {
        const player = DzGetTriggerKeyPlayer();
        const playerId = GetPlayerId(player);
        const playerModel: PlayerModel = PlayerFactory.getInstance().getPlayer(playerId)
        return { player: playerModel, key: DzGetTriggerKey() };
    }

    getLevelUpInfo(): LevelUpInfo {
        const activeUnit = GetLevelingUnit();
        const activeUnitId = GlobalModel.getHandleId(activeUnit);
        const activeUnitModel: UnitModel = ModelFactory.getInstance().getModel(activeUnitId, ModelType.unit) as UnitModel;
        const level = GetHeroLevel(activeUnit);
        return { unit: activeUnitModel, level };
    }

    getManipulatingItemInfo(): ManipulatingItemInfo {
        const item = GetManipulatedItem();
        const activeUnit = GetManipulatingUnit();
        const unitId = GlobalModel.getHandleId(activeUnit);
        const itemId = GlobalModel.getHandleId(item);
        const unitModel: UnitModel = ModelFactory.getInstance().getModel(unitId, ModelType.unit) as UnitModel;
        const itemModel: ItemModel = ModelFactory.getInstance().getModel(itemId, ModelType.item) as ItemModel;
        const activePlayerModel: PlayerModel = PlayerFactory.getInstance().getUnitOwner(unitModel) as PlayerModel;

        if (itemModel) {
        } else {
            if (IsHandle(item)) {
                // Logger.toastError(`删除违规物品! ${GetItemName(item)}`, activePlayerModel);
                RemoveItem(item);
            }
            return { unit: unitModel, item: undefined };
        }

        return { unit: unitModel, item: itemModel };
    }

    getPlayerExitInfo(): PlayerExitInfo {
        const player = GetTriggerPlayer();
        const playerId = GetPlayerId(player);
        const playerModel: PlayerModel = PlayerFactory.getInstance().getPlayer(playerId)
        return { player: playerModel };
    }

    getSelectInfo(): SelectInfo {
        const player = GetTriggerPlayer();
        const playerId = GetPlayerId(player);
        const playerModel = PlayerFactory.getInstance().getPlayer(playerId) as PlayerModel;
        return { player: playerModel };
    }

    getSellItemInfo(): SellItemInfo {
        const item = GetSoldItem();
        const activeUnit = GetBuyingUnit();
        const sellerUnit = GetSellingUnit();
        const unitId = GlobalModel.getHandleId(activeUnit);
        const itemId = GlobalModel.getHandleId(item);
        const sellerId = GlobalModel.getHandleId(sellerUnit);
        const unitModel: UnitModel = ModelFactory.getInstance().getModel(unitId, ModelType.unit) as UnitModel;
        const sellerUnitModel: UnitModel = ModelFactory.getInstance().getModel(sellerId, ModelType.unit) as UnitModel;
        let itemModel: ItemModel = ModelFactory.getInstance().getModel(itemId, ModelType.item) as ItemModel;

        if (sellerUnitModel) {
        } else {
            Logger.toastError(`违规的售卖者!`);
            return { buyUnit: undefined, item: undefined, sellUnit: undefined };
        }

        if (itemModel) {
        } else {
            if (IsHandle(item)) {
                // Logger.toastError(`违规的物品!`);
                itemModel = ModelFactory.getInstance().registItem(item);
            }
        }

        return { buyUnit: unitModel, item: itemModel, sellUnit: sellerUnitModel };
    }

    getSpellInfo(): SpellInfo {
        const activeUnit = GetSpellAbilityUnit();
        const targetUnit = GetSpellTargetUnit();
        const activeItem = GetManipulatedItem();
        const targetItem = GetSpellTargetItem();

        const activeUnitId = GlobalModel.getHandleId(activeUnit);
        const targetUnitId = GlobalModel.getHandleId(targetUnit);
        const activeItemId = GlobalModel.getHandleId(activeItem);
        const targetItemId = GlobalModel.getHandleId(targetItem);
        const activeUnitModel: UnitModel = ModelFactory.getInstance().getModel(activeUnitId, ModelType.unit) as UnitModel;
        const targetUnitModel: UnitModel = ModelFactory.getInstance().getModel(targetUnitId, ModelType.unit) as UnitModel;
        const activeItemModel: ItemModel = ModelFactory.getInstance().getModel(activeItemId, ModelType.item) as ItemModel;
        const targetItemModel: ItemModel = ModelFactory.getInstance().getModel(targetItemId, ModelType.item) as ItemModel;

        const spellId = id2string(GetSpellAbilityId());
        const loc = new LocModel(GetSpellTargetX(), GetSpellTargetY());

        return { useUnit: activeUnitModel, targetUnit: targetUnitModel, targetItem: targetItemModel, spellId, spellLoc: loc, useItem: activeItemModel };
    }

    getMouseEventInfo(): MouseEventInfo {
        const startX = DzGetWindowX();
        const startY = DzGetWindowY();
        const screenX = DzGetMouseX();
        const screenY = DzGetMouseY();
        // const gameX = DzGetMouseXRelative();
        // const gameY = DzGetMouseYRelative();
        const loc = new LocModel(DzGetMouseTerrainX(), DzGetMouseTerrainY(), DzGetMouseTerrainZ());
        const triggerPlayer = DzGetTriggerKeyPlayer();
        const playerId = GetPlayerId(triggerPlayer);
        const playerModel: PlayerModel = PlayerFactory.getInstance().getPlayer(playerId)
        const eventType: MouseEventType = mouseEventType;
        const width = DzGetWindowWidth();
        const height = DzGetWindowHeight();

        const targetUnit = DzGetUnitUnderMouse();
        const targetUnitId = GlobalModel.getHandleId(targetUnit);
        const targetUnitModel: UnitModel = ModelFactory.getInstance().getModel(targetUnitId, ModelType.unit) as UnitModel;


        const relaX = screenX - startX;
        const relaY = screenY - startY;
        const percentX = (relaX) / (width - 9);
        const percentY = (relaY) / (height + 10);

        return {
            percentX,
            percentY,
            // startX,
            // startY,
            // screenX,
            // screenY,
            player: playerModel,
            loc,
            type: eventType,
            unit: targetUnitModel,
            // width: width,
            // height: height,
            // gameY,
            // gameX
        }
    }

    getSyncDataInfo(): SyncDataInfo {
        let triggerPlayer = DzGetTriggerSyncPlayer();
        const playerId = GetPlayerId(triggerPlayer);
        const playerModel: PlayerModel = PlayerFactory.getInstance().getPlayer(playerId)
        let dataStr = DzGetTriggerSyncData();
        return { player: playerModel, str: dataStr }
    }

    getTransInfo(): TransInfo {
        const transUnit = GetTriggerUnit();
        const region = GetTriggeringRegion();
        const activeUnitId = GlobalModel.getHandleId(transUnit);
        const regionId = GlobalModel.getHandleId(region);
        const activeUnitModel: UnitModel = ModelFactory.getInstance().getModel(activeUnitId, ModelType.unit) as UnitModel;

        const rectName = rectMap[regionId];
        let rect = ModelFactory.getInstance().getModel(rectName, ModelType.rect) as RectModel;
        return { transUnit: activeUnitModel, rect };
    }

    getWheelInfo(): WheelInfo {
        const wheelNum = DzGetWheelDelta();
        // const player = DzGetTriggerKeyPlayer();
        // const playerId = GetPlayerId(player);
        const playerModel = PlayerFactory.getInstance().getLocalPlayer();
        return { player: playerModel, delta: wheelNum };
    }

    getUpdateInfo(): UpdateInfo {
        const playerModel = PlayerFactory.getInstance().getLocalPlayer();
        return { player: playerModel };
    }

    syncData(key: string, str: string) {
        DzSyncData(key, str)
    }

}
